use sqlx::{sqlite::SqliteRow, Row};

pub const NL: &str = "\n";

#[derive(serde::Serialize, serde::Deserialize)]
pub struct Connection {
    pub id: Option<String>,
    pub name: String,
    pub host: String,
    pub port: u32,
    pub username: String,
    pub password: String,
    pub database: String,
    // schema: String,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct DbConnection {
    pub id: String,
    pub database: String,
    pub schema: String,
}

// macro_rules! read_string_value {
//     ($key:expr,$data:expr) => {{
//         let item = $data.iter().find(|x| x.0 == $key);
//         if let Some(item) = item {
//             item.1.unwrap_or("").to_string()
//         } else {
//             String::new()
//         }
//     }};
// }

impl Connection {
    // pub fn from(row: &[(&str, Option<&str>)]) -> Connection {
    //     Connection {
    //         id: Some(read_string_value!("Id", row)),
    //         name: read_string_value!("Name", row),
    //         host: read_string_value!("Host", row),
    //         port: read_string_value!("Port", row)
    //             .parse::<u32>()
    //             .unwrap_or(5432),
    //         username: read_string_value!("UserName", row),
    //         password: read_string_value!("Password", row),
    //         // database: read_string_value!("Database", row),
    //         // schema: read_string_value!("Schema", row),
    //     }
    // }

    pub fn from_row(row: SqliteRow) -> Connection {
        Connection {
            id: Some(row.get("Id")),
            name: row.get("Name"),
            host: row.get("Host"),
            port: row.get::<i64, &str>("Port") as u32,
            username: row.get("UserName"),
            password: row.get("Password"),
            database: row.get("Database"),
        }
    }
}

pub type AppResult<T> = Result<T, AppError>;

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AppError {
    pub code: Option<String>,
    pub message: Option<String>,
}

impl AppError {
    pub fn new() -> Self {
        Self {
            code: None,
            message: None,
        }
    }

    pub fn code(mut self, code: &str) -> Self {
        self.code = Some(code.to_string());
        self
    }

    pub fn message(mut self, message: &str) -> Self {
        self.message = Some(message.to_string());
        self
    }

    #[allow(dead_code)]
    pub fn to_string(&self) -> String {
        let mut result = String::new();
        if let Some(code) = self.code.as_ref() {
            result.push_str(&format!("code = {}, ", code));
        }
        if let Some(message) = self.code.as_ref() {
            result.push_str(message);
        }
        result
    }
}

impl From<sqlx::Error> for AppError {
    fn from(err: sqlx::Error) -> Self {
        Self::new()
            .code("SQL Error")
            .message(&format!("{}", err.to_string()))
    }
}

impl From<std::io::Error> for AppError {
    fn from(err: std::io::Error) -> Self {
        Self::new()
            .code("IO Error")
            .message(&format!("{}", err.to_string()))
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct SimpleConnection {
    pub id: String,
    pub name: String,
    pub host: String,
    pub port: u32,
}

impl SimpleConnection {
    pub fn from_row(row: SqliteRow) -> Self {
        Self {
            id: row.get("Id"),
            name: row.get("Name"),
            host: row.get("Host"),
            port: row.get::<i64, &str>("Port") as u32,
        }
    }
}
